using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;

using EmbeddedWebserver.Core;
using EmbeddedWebserver.Core.Handlers.Abstract;
using EmbeddedWebserver.Core.Helpers;

using System.Collections;

namespace HttpIntfLib
{
    class GpioDriver : HandlerBase
    {
        #region Non-public members

        /* 
         * gpio?pin=[0-16]
         * gpio?pin=[1-16]&Val=16bitbask         
         * gpio?OnBrdLed
         * gpio?OnBrdLed=1
         * gpio?OnBrdButton
         */
        static private string PINSTR          = "pin";
        static private string VALUESTR        = "value";

        protected override void ProcessRequestWorker(HttpContext pContext)
        {
            short  pinNum = -1;
            short  value=-1;
            string tmpStr=null;

            pContext.Request.QueryString.DebugPrint();
            pContext.Request.RequestHeaders.DebugPrint(); 
            try
            {
                if (pContext.Request.QueryString.Count > 0)
                {
                    if (pContext.Request.QueryString.ContainsKey(PINSTR))
                    {
                        tmpStr = pContext.Request.QueryString[PINSTR];
                        pinNum = Convert.ToInt16(tmpStr);

                        if (pinNum < 0)
                        {
                            Debug.Print("Wrong Pin Number " + tmpStr);
                            pContext.Response.ResponseBody = FormatHelpers.getErrJson(-1, "Error Pin Number" + tmpStr);
                            pContext.Response.ContentType = "application/json";
                            return;
                        }
                    }

                    if (pContext.Request.QueryString.ContainsKey(VALUESTR))
                    {
                        tmpStr = pContext.Request.QueryString[VALUESTR];
                        value = Convert.ToInt16(tmpStr);
                    }

                    //  let us Do the process. 
                    //1. Find out input/out put
                    Cpu.Pin pin = (Cpu.Pin) pinNum; 

                    if (value < 0)
                    {
                        // Read  Now 
                        TristatePort gpioInput = PortObjectStore.GetInputPortObject(pin, Port.ResistorMode.PullUp);
                        if (gpioInput != null)
                        {
                            bool inputVal = gpioInput.Read();
                            pContext.Response.ResponseBody = FormatHelpers.getJson(pinNum, inputVal);
                            pContext.Response.ContentType = "application/json";
                        }
                        else
                        {
                            Debug.Print("Failed to read Input pin " + pin);
                            pContext.Response.ResponseBody = FormatHelpers.getErrJson(-1, "Failed to read Input pin" + pin);
                            pContext.Response.ContentType = "application/json";
                        }
                    }
                    else
                    {
                        // Write Now
                                               
                        bool bOutput = (value > 0);
                        Debug.Print(" Input Pin : " + pinNum + "Input " + pin + "Value :"+bOutput);
                        TristatePort gpioOutput = PortObjectStore.GetOutputPortObject(pin);

                        if( gpioOutput != null )
                           gpioOutput.Write(bOutput);                       

                        pContext.Response.ResponseBody = FormatHelpers.getJson(pinNum, (value > 0));
                        pContext.Response.ContentType = "application/json";
                    }                   
                }
                else
                {
                    pContext.Response.ResponseBody = FormatHelpers.getErrJson(-3, "please provide Pin number");
                    pContext.Response.ContentType = "application/json";
                }
            }
            catch (Exception e)
            {
                Debug.Print("Error in Execution.." + e.StackTrace);
                pContext.Response.ResponseBody = FormatHelpers.getErrJson(-2, e.StackTrace);
                pContext.Response.ContentType = "application/json";
            }
           
        }

        #endregion

        #region Constructors
        
        public GpioDriver() : base(HttpMethods.GET) 
        {        }

        #endregion
    }


    // Port Object Store .. 
    public class PortObjectStore
    {
        // Plan to Use TriState Port 

        private static Hashtable gpioOutputStore = new Hashtable();

        private static Cpu.Pin[] pinCodes = new Cpu.Pin[] { Pins.GPIO_PIN_D0, Pins.GPIO_PIN_D1, Pins.GPIO_PIN_D2, Pins.GPIO_PIN_D3, Pins.GPIO_PIN_D4, Pins.GPIO_PIN_D5, Pins.GPIO_PIN_D6, Pins.GPIO_PIN_D7, 
                                                          Pins.GPIO_PIN_D8, Pins.GPIO_PIN_D9, Pins.GPIO_PIN_D10, Pins.GPIO_PIN_D11, Pins.GPIO_PIN_D12, Pins.GPIO_PIN_D13, Pins.ONBOARD_LED, Pins.ONBOARD_BTN };

        // Get Output Port 
        public static TristatePort GetOutputPortObject(Cpu.Pin pin, Port.ResistorMode resistorMode = Port.ResistorMode.PullUp)
        {
            TristatePort pinObj = GetPortObject(pin, resistorMode);
            if (pinObj != null)
            {
                if (!pinObj.Active)
                {
                    pinObj.Active = true;
                }
            }
            return pinObj;
        }


        // get Input Object 
        public static TristatePort GetInputPortObject(Cpu.Pin pin, Port.ResistorMode resistorMode = Port.ResistorMode.PullUp)
        {
            TristatePort pinObj = GetPortObject(pin, resistorMode);
            if (pinObj != null)
            {
                if (pinObj.Active)
                {
                    pinObj.Active = false;
                }
            }
            return pinObj;
        }


        // Get Port Object 
        private  static TristatePort GetPortObject(Cpu.Pin pin,Port.ResistorMode resistorMode=Port.ResistorMode.PullUp)
        {

            if ((short) pin > pinCodes.Length)
            {
                Debug.Print(" Invalid Pin code " + pin);
                return null;
            }

            Cpu.Pin actutalPincode = pinCodes[(short)pin];

            TristatePort pinObj = gpioOutputStore[actutalPincode] as TristatePort;
            
            if (pinObj == null)
            {
                TristatePort portObj = new TristatePort(actutalPincode, false, false, resistorMode);
                gpioOutputStore.Add(actutalPincode, portObj);
                return portObj;
            }
            else
            {
                return pinObj;
            }

        }

        

    }
}
